

package com.rollway.restful;

import com.rollway.restful.options.RestfulOptions;
import com.rollway.restful.options.CompositeRestfulOptions;
import com.rollway.restful.support.MethodMetadataParser;
import com.rollway.restful.utils.Util;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.http.converter.HttpMessageConverter;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class RestfulClientFactoryBean implements FactoryBean<Object> {

    private Class<?> type;

    private RestfulOptions restfulOptions = new RestfulOptions();

    private MethodMetadataParser methodMetadataParser;

    private CompositeRestfulOptions compositeRestfulOptions;

    private List<RequestCustomizer> requestCustomizers;

    private OkHttpClient okHttpClient;

    private List<HttpMessageConverter<?>> httpMessageConverters;

    @Override
    public Object getObject() {
        CompositeRestfulOptions.CompositeRestfulMethodOptions methodOptionsComposite = new CompositeRestfulOptions.CompositeRestfulMethodOptions();
        methodOptionsComposite.put(Util.DEFAULT, this.restfulOptions);
        compositeRestfulOptions.put(this.restfulOptions.getServiceName(), methodOptionsComposite);
        List<MethodMetadata> methodMetadataList = methodMetadataParser.parseAndValidateMetadata(type, restfulOptions);
        Map<String, MethodHandler> nameToHandler = getMethodHandlerMap(methodMetadataList);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<>();
        for (Method method : type.getMethods()) {
            if (method.getDeclaringClass() == Object.class) {
                continue;
            } else if (Util.isDefault(method)) {
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                methodToHandler.put(method, nameToHandler.get(Util.configKey(type, method)));
            }
        }
        InvocationHandler handler = new RestfulInvocationHandler(restfulOptions.getServiceName(), methodToHandler);
        Object proxy = Proxy.newProxyInstance(type.getClassLoader(),
                new Class<?>[]{type}, handler);
        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
            defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }

    public Map<String, MethodHandler> getMethodHandlerMap(List<MethodMetadata> methodMetadataList) {
        Map<String, MethodHandler> result = new LinkedHashMap<>();
        for (MethodMetadata md : methodMetadataList) {
            result.put(md.getMethodKey(), new RestfulMethodHandler(md, compositeRestfulOptions,okHttpClient,httpMessageConverters,requestCustomizers));
        }
        return result;
    }

    @Override
    public Class<?> getObjectType() {
        return type;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public RestfulOptions getRestfulOptions() {
        return restfulOptions;
    }

    public void setRestFulOptionsComposite(CompositeRestfulOptions compositeRestFulOptions) {
        this.compositeRestfulOptions = compositeRestFulOptions;
    }

    public void setOkHttpClient(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
    }

    public void setMethodMetadataParser(MethodMetadataParser methodMetadataParser) {
        this.methodMetadataParser = methodMetadataParser;
    }

    public void setHttpMessageConverters(List<HttpMessageConverter<?>> httpMessageConverters) {
        this.httpMessageConverters = httpMessageConverters;
    }

    public void setRequestCustomizers(List<RequestCustomizer> requestCustomizers) {
        this.requestCustomizers = requestCustomizers;
    }
}
